<?php

namespace Plugins\AdminBasic\Http\Admin\Service;

use Plugins\AdminBasic\Http\Admin\Service\MakeCURDFiles\MakeController;
use Plugins\AdminBasic\Http\Admin\Service\MakeCURDFiles\MakeEnum;
use Plugins\AdminBasic\Http\Admin\Service\MakeCURDFiles\MakeModel;
use Plugins\AdminBasic\Http\Admin\Service\MakeCURDFiles\MakeRequest;
use Plugins\AdminBasic\Http\Admin\Service\MakeCURDFiles\MakeService;
use Plugins\AdminBasic\Http\Admin\Service\MakeCURDFiles\MakeView;
use App\Models\Model;
use App\Util\Helper;
use Hyperf\Stringable\Str;
use Sc\Util\ScTool;
use Sc\Util\Tool;

/**
 * Class MakeCURDService
 */
class MakeCURDService
{
    const TEMPLATE_DIR = __DIR__ . '/BaseFileTemplate/';
    const BASE_FILE_DIR = [
        'Controller'  => BASE_PATH . '/app/Http/Admin/Controller/%sController.php',
        'Model'       => BASE_PATH . '/app/Models/%s.php',
        'Service'     => BASE_PATH . '/app/Http/Admin/Service/%sService.php',
        'FormRequest' => BASE_PATH . '/app/Requests/%sRequest.php',
        'Enum'        => BASE_PATH . '/app/Enums/%s.php',
        'View'        => BASE_PATH . '/storage/view/Admin/%s.sc.php',
    ];

    const BASE_PLUGINS_FILE_DIR = [
        'Controller'  => BASE_PATH . '/plugins/:plugins/Http/Admin/Controller/%sController.php',
        'Model'       => BASE_PATH . '/plugins/:plugins/Models/%s.php',
        'Service'     => BASE_PATH . '/plugins/:plugins/Http/Admin/Service/%sService.php',
        'FormRequest' => BASE_PATH . '/plugins/:plugins/Requests/%sRequest.php',
        'Enum'        => BASE_PATH . '/plugins/:plugins/Enums/%s.php',
        'View'        => BASE_PATH . '/plugins/:plugins/Http/Admin/View/%s.sc.php',
    ];

    protected mixed $files;
    protected mixed $controllerMethod;
    protected mixed $table;
    protected mixed $tableComment;
    protected mixed $dir;
    protected mixed $viewType;
    protected mixed $windowType;
    protected mixed $fields;
    protected array $commonReplaceVar;
    /**
     * @var array|string|string[]|null
     */
    private string|array|null $baseFileName;
    private bool $isCover;
    private array $statusMapTranslationRes;
    private string $plugins;
    private bool $isPlugins;
    /**
     * @var array|string[]
     */
    private array $baseFileDir;
    private mixed $windowWidth;

    public function __construct(array $makeData)
    {
        $this->isCover          = (bool)$makeData['isCover'];
        $this->files            = $makeData['files'];
        $this->controllerMethod = $makeData['controllerMethod'];
        $this->table            = DevAuxService::getTable($makeData['table'], false);
        $this->tableComment     = $makeData['tableComment'];
        $this->dir              = $makeData['dir'];
        $this->viewType         = $makeData['viewType'];
        $this->fields           = $makeData['fields'];
        $this->windowType       = $makeData['windowType'];
        $this->windowWidth      = $makeData['windowWidth'];
        $this->isPlugins        = $makeData['isPlugins'];
        $this->plugins          = $makeData['plugins'];
        $this->baseFileDir      = $this->isPlugins ? self::BASE_PLUGINS_FILE_DIR : self::BASE_FILE_DIR;

        foreach ($this->fields as &$field) {
            $this->fieldStatusMapHandle($field);
            $this->fieldJoinHandle($field);
        }

        $this->CommonReplaceVarHandle();
        $this->statusMapTranslationRes = self::statusMapTranslation($this->fields);
    }

    /**
     * @param string $table
     * @param string $dir
     * @param string $plugins
     *
     * @return array
     * @throws \Exception
     */
    public static function check(string $table, string $dir, string $plugins): array
    {
        $name = Str::studly(DevAuxService::getTable($table, false));
        $baseFileName = preg_replace('/\/+/', '/', $dir  . '/' . $name);

        // 基础文件
        $baseDir = $plugins ? self::BASE_PLUGINS_FILE_DIR : self::BASE_FILE_DIR;
        $exists = [];
        foreach ($baseDir as $mode => $template){
            $template = self::getTemplate($template, $plugins);
            if (file_exists($filename = sprintf($template, $baseFileName))) {
                $exists[] = [
                    'mode'     => $mode,
                    'filename' => $filename,
                ];
            }
        }

        // 视图文件
        foreach (['form', 'lists'] as $view){
            $filename = preg_replace('/\/+/', '/', $dir . '/' . $name . '/' . $view);
            $filename = sprintf(self::getTemplate($baseDir['View'], $plugins), $filename);

            if (file_exists($filename)) {
                $exists[] = [
                    'mode'     => "View:" . $view,
                    'filename' => $filename,
                ];
            }
        }

        // 枚举文件
        $enumDir = strtr(self::getTemplate($baseDir["Enum"], $plugins), ['%s.php' => '']);
        if (is_dir($enumDir)) {
            Tool::dir($enumDir)->each(function (Tool\Dir\EachFile $file) use ($dir, $name, &$exists){
                if (preg_match("/^{$name}Enum(\w+)/", $file->filename, $match) && implode('/', $file->relativelyDirs) == $dir) {
                    $exists[] = [
                        'mode'     => "Enum: " . $match[1],
                        'filename' => $file->filepath,
                    ];
                }
            });
        }

        return $exists;
    }

    /**
     * @param string $plugins
     *
     * @return array
     */
    public static function getCurrentDirs(string $plugins = ''): array
    {
        try {
            return $plugins
                ? Tool::dir(BASE_PATH . "/plugins/$plugins/Http/Admin/Controller")->getDirs()
                : Tool::dir(BASE_PATH . '/app/Http/Admin/Controller')->getDirs();
        } catch (\Throwable) {
            return [];
        }
    }

    /**
     * @param $field
     *
     * @return void
     */
    private function fieldStatusMapHandle(&$field): void
    {
        if (empty($field['status_map'])) {
            $field['status_map'] = [];
            return;
        }

        $field['status_map'] = array_map(function ($mapStr) {
            list($key, $value) = explode('=', $mapStr);
            return compact('key', 'value');
        }, $field['status_map']);
    }

    /**
     * @param $field
     *
     * @return void
     */
    private function fieldJoinHandle(&$field): void
    {
        if (empty($field['join_field'])) {
            return;
        }

        if (preg_match('/^(\w+):(\w+)=(\w+)$/', $field['join_field'], $match)) {
            $field['join_field'] = [
                'table' => preg_replace("/^". Model::getPrefix() ."/", '', $match[1]),
                'key'   => $match[2],
                'label' => $match[3],
            ];
        }else{
            $field['join_field'] = null;
        }
    }

    /**
     * @param array $allFields
     *
     * @return array
     */
    public static function statusMapTranslation(array $allFields): array
    {
        $map = array_column($allFields, 'status_map');
        $map = array_merge(...array_filter($map));
        $waitTranslation = array_unique(array_column($map, 'value'));
        if (!$waitTranslation){
            return [];
        }

        $result = ScTool::baiduFanYi($waitTranslation)
            ->setAccount(Helper::getConfig('baidu.translation.appid'), Helper::getConfig('baidu.translation.secret'))
            ->toEn();

        $result = array_map(function ($item) {
            $item['dst'] = Str::studly($item['dst']);
            return $item;
        }, $result);

        return array_column($result, 'dst', 'src');
    }

    /**
     * 清除文件
     *
     * @param array $files
     *
     * @return void
     */
    public static function clear(array $files): void
    {
        foreach ($files as $file) {
            file_exists($file) and unlink($file);
        }
    }

    /**
     * @param string $template
     * @param string $plugins
     *
     * @return string
     */
    public static function getTemplate(string $template, string $plugins): string
    {
        return $plugins ? strtr($template, [':plugins' => $plugins]) : $template;
    }


    public function make(): array
    {
        $res = [];

        foreach ($this->files as $file) {
            $res[] = match ($file){
                'Controller'  => "Controller: " . (new MakeController($this))->make(),
                'Model'       => "Model: " . (new MakeModel($this))->make(),
                'Service'     => "Service: " . (new MakeService($this))->make(),
                'FormRequest' => "FormRequest: " . (new MakeRequest($this))->make(),
            };
        }

        $makeEnum = new MakeEnum($this);
        $makeEnum->make();
        $makeView = new MakeView($this);
        $makeView->make();

        return array_merge($res, $makeEnum->getMakeRes(), $makeView->getMakeRes());
    }

    /**
     * @return void
     */
    private function CommonReplaceVarHandle(): void
    {
        $this->commonReplaceVar = [
            'dir_namespace'  => $this->getAddPrefixStr(strtr($this->dir, ['/' => '\\']), '\\'),
            'base_namespace' => $this->isPlugins ? "Plugins\\$this->plugins" : 'App',
            'dir'            => $this->dir,
            'name'           => Str::studly($this->table),
            'name_kebab'     => Str::kebab($this->table),
            'name_lcfirst'   => lcfirst($this->table),
            'comment'        => $this->tableComment,
            'plugins_view'   => $this->isPlugins ? "plugins.$this->plugins." : '',
            'plugins_c'      => $this->isPlugins ? 'plugins/' : '',
        ];

        $this->baseFileName = preg_replace('/\/+/', '/', $this->dir  . '/' . $this->commonReplaceVar['name']);
    }

    /**
     * @param string $str
     * @param string $prefix
     *
     * @return string
     */
    public function getAddPrefixStr(string $str, string $prefix): string
    {
        return ($str ? $prefix : '') . $str;
    }

    public function attrGetter(string $name, mixed $default = null)
    {
        if (property_exists($this, $name)) {
            return $this->$name;
        }

        return $default;
    }
}